////////////////////////////////////////////////////////////////////////////////////
//
//
//							MiTAC Corporation
//				NO. 187, TIDING BLVD., SEC. 2, TAIPEI, TAIWAN
//
// 			(c) Copyright 2006, MiTAC Corporation, Taipei, Taiwan
//						All rights reserved.
//-------------------------------------------------------------------------------------
//
//	File:	intr.c
//	Description:	OMAP850 interrupt handler
//
//	Programer: Visual.Wei
//	Date: 2006-3-20
//
//////////////////////////////////////////////////////////////////////////////
#include <windows.h>
#include <bsp.h>
#include <s3c2443.h>
#include "kernel_asm.h"
#include "ipl_core.h"


///////////////////////////////
//
// print debug message to screen
///////////////////////////////
//#undef RETAILMSG
//#undef DEBUGMSG
//#define  RETAILMSG(cond,txt) LCDTermPrintf txt
//#define DEBUGMSG RETAILMSG
//debug message show
#define INTR_INFO 0


static S3C2443_INTR_REG *g_pIntrRegs;
static S3C2443_IOPORT_REG *g_pPortRegs;

static ISR g_isr_func[200];

volatile static void swi_handler()
{
	RETAILMSG (INTR_INFO, (TEXT(">>>INT: swi_handler\r\n")));
}

volatile static void prefetch_handler()
{
	RETAILMSG (INTR_INFO, (TEXT(">>>INT: Prefetch_handler\r\n")));
	while(1)
		;
}

volatile static void data_handler(struct regs * regs)
{
	OALMSG(1,(L">>>INT: DataAbort_handler\r\n"));
	OALMSG(1,(L">>>>FSR: 0x%x\r\n",regs->cp_fsr));
	OALMSG(1,(L">>>>FAR: 0x%x\r\n",regs->cp_far));
	OALMSG(1,(L">>>>PC : 0x%x\r\n",regs->pc));
	//printk(L">>>INT: DataAbort_handler\r\n");
	//printk(L">>>>FSR: 0x%x\r\n",regs->cp_fsr);
	//printk(L">>>>FAR: 0x%x\r\n",regs->cp_far);
	//printk(L">>>>PC : 0x%x\r\n",regs->pc);
	while(1)
		;
}
static UINT32 OEMIntHandlerTopHalf(UINT32 *bmapedirq)
{
	UINT32 irq;
	UINT32 pend,irq2,i;
	UINT32 mask;
	
	irq = INREG32(&g_pIntrRegs->INTOFFSET);
	RETAILMSG (INTR_INFO, (TEXT("int: %d\r\n"),irq));

	*bmapedirq=irq;
	
	if((irq == IRQ_EINT4_7)||(irq == IRQ_EINT8_23))
	{
		pend = INREG32(&g_pPortRegs->EINTPEND);
		RETAILMSG (0, (TEXT("pending: 0x%x\r\n"),pend));
		//pend &= ~INREG32(&g_pPortRegs->EINTMASK);
		for(i=4,irq2 = IRQ_EINT4;i<=23;i++,irq2++)
		{
			if(pend&(1<<i))
			{
				*bmapedirq=irq2;
				break;
			}
		}
		//mask the current gpio interrupt
		mask = 1 << (irq2 - IRQ_EINT4 + 4);
		SETREG32(&g_pPortRegs->EINTMASK, mask);
		OUTREG32(&g_pPortRegs->EINTPEND, mask);
	}
 
	return irq;
}

static void OEMIntHandlerBottomHalf(UINT32 irq,UINT32 mapirq)
{
	UINT32 mask,intr;

	//sub interrupt
	switch(irq)
	{
	case  IRQ_WDT_AC97:
		intr=INREG32(&g_pIntrRegs->SUBSRCPND);
		if(intr&(1<<IRQ_SUB_WDT))
			OUTREG32(&g_pIntrRegs->SUBSRCPND, (1<<IRQ_SUB_WDT));
		else if(intr&(1<<IRQ_SUB_AC97))
			OUTREG32(&g_pIntrRegs->SUBSRCPND, (1<<IRQ_SUB_AC97));
	case  IRQ_CAM:
		intr=INREG32(&g_pIntrRegs->SUBSRCPND);
		if(intr&(1<<IRQ_SUB_CAM_C))
			OUTREG32(&g_pIntrRegs->SUBSRCPND, (1<<IRQ_SUB_CAM_C));
		else if(intr&(1<<IRQ_SUB_CAM_P))
			OUTREG32(&g_pIntrRegs->SUBSRCPND, (1<<IRQ_SUB_CAM_P));
	case  IRQ_ADC:
		intr=INREG32(&g_pIntrRegs->SUBSRCPND);
		if(intr&(1<<IRQ_SUB_ADC))
			OUTREG32(&g_pIntrRegs->SUBSRCPND, (1<<IRQ_SUB_ADC));
		else if(intr&(1<<IRQ_SUB_TC))
			OUTREG32(&g_pIntrRegs->SUBSRCPND, (1<<IRQ_SUB_TC));

	case  IRQ_UART2:
		intr=INREG32(&g_pIntrRegs->SUBSRCPND);
		if(intr&(1<<IRQ_SUB_RXD2))
			OUTREG32(&g_pIntrRegs->SUBSRCPND, (1<<IRQ_SUB_RXD2));
		else if(intr&(1<<IRQ_SUB_TXD2))
			OUTREG32(&g_pIntrRegs->SUBSRCPND, (1<<IRQ_SUB_TXD2));
		else if(intr&(1<<IRQ_SUB_ERR2))
			OUTREG32(&g_pIntrRegs->SUBSRCPND, (1<<IRQ_SUB_ERR2));

	case  IRQ_UART1:
		intr=INREG32(&g_pIntrRegs->SUBSRCPND);
		if(intr&(1<<IRQ_SUB_RXD1))
			OUTREG32(&g_pIntrRegs->SUBSRCPND, (1<<IRQ_SUB_RXD1));
		else if(intr&(1<<IRQ_SUB_TXD1))
			OUTREG32(&g_pIntrRegs->SUBSRCPND, (1<<IRQ_SUB_TXD2));
		else if(intr&(1<<IRQ_SUB_ERR1))
			OUTREG32(&g_pIntrRegs->SUBSRCPND, (1<<IRQ_SUB_ERR1));

	case  IRQ_UART0:
		intr=INREG32(&g_pIntrRegs->SUBSRCPND);
		if(intr&(1<<IRQ_SUB_RXD0))
			OUTREG32(&g_pIntrRegs->SUBSRCPND, (1<<IRQ_SUB_RXD0));
		else if(intr&(1<<IRQ_SUB_TXD0))
			OUTREG32(&g_pIntrRegs->SUBSRCPND, (1<<IRQ_SUB_TXD0));
		else if(intr&(1<<IRQ_SUB_ERR0))
			OUTREG32(&g_pIntrRegs->SUBSRCPND, (1<<IRQ_SUB_ERR0));
	}

	
	mask = 1 << irq;
	if((irq == IRQ_EINT4_7)||(irq == IRQ_EINT8_23))
		CLRREG32(&g_pIntrRegs->INTMSK, mask);
	else
		SETREG32(&g_pIntrRegs->INTMSK, mask);
	OUTREG32(&g_pIntrRegs->SRCPND, mask);
	OUTREG32(&g_pIntrRegs->INTPND, mask);

}

volatile static void irq_handler()
{
	UINT32 irq;
	UINT32 mapirq;
	
	RETAILMSG (INTR_INFO, (TEXT(">>>INT: irq_handler\r\n")));
	irq=OEMIntHandlerTopHalf(&mapirq);
		
	OEMIntHandlerBottomHalf(irq,mapirq);

	if(g_isr_func[mapirq])
		g_isr_func[mapirq]();//call isr
}

volatile static void fiq_handler()
{
	RETAILMSG (INTR_INFO, (TEXT(">>>INT: fiq_handler\r\n")));
}
static void OEMIntrInit()
{
	OALMSG( INTR_INFO, (L"OEMIntrInit\r\n") );

	// First get uncached virtual addresses
	g_pIntrRegs = (S3C2443_INTR_REG*)OALPAtoVA(S3C2443_BASE_REG_PA_INTR, FALSE);
	g_pPortRegs = (S3C2443_IOPORT_REG*)OALPAtoVA(S3C2443_BASE_REG_PA_IOPORT, FALSE);

	// GPIO Mask and clear external interrupts
	OUTREG32(&g_pPortRegs->EINTMASK, 0xFFFFFFFF);
	OUTREG32(&g_pPortRegs->EINTPEND, 0xFFFFFFFF);

	// Mask and clear internal interrupts
	OUTREG32(&g_pIntrRegs->INTMSK, 0xFFFFFFFF);
	OUTREG32(&g_pIntrRegs->SRCPND, 0xFFFFFFFF);

	// S3C2440X developer notice (page 4) warns against writing a 1 to any
	// 0 bit field in the INTPND register.  Instead we'll write the INTPND
	// value itself.
	OUTREG32(&g_pIntrRegs->INTPND, INREG32(&g_pIntrRegs->INTPND));

}
//------------------------------------------------------------------------------
//
//  Function:  IPLIntrInit
//
//
void IPLIntrInit()
{
	BOOL rc = FALSE;
	UINT32 i,data;
	
	RETAILMSG(INTR_INFO, (L"+IPLIntrInit\r\n"));

	//init ISR function table
	for(i=0;i<200;i++)
		g_isr_func[i]=0;

	RETAILMSG(INTR_INFO, (L"_trap_init()...\r\n"));
	_trap_init();

	RETAILMSG (INTR_INFO, (TEXT("trap_init() ok\r\n")));

	//check magic number
	data=INREG32((void *)(INTR_VEC_PHY_BASE+0x20));
	RETAILMSG (INTR_INFO, (TEXT("magic:0x%x relocation OK!!\r\n"),data));

	//install function table
	OUTREG32((void *)(INTR_VEC_PHY_BASE+0x2c),swi_handler);
	OUTREG32((void *)(INTR_VEC_PHY_BASE+0x30),prefetch_handler);
	OUTREG32((void *)(INTR_VEC_PHY_BASE+0x34),data_handler);
	OUTREG32((void *)(INTR_VEC_PHY_BASE+0x3c),irq_handler);
	OUTREG32((void *)(INTR_VEC_PHY_BASE+0x40),fiq_handler);
	//data=INREG32(0xFFFF002C);
	//RETAILMSG (1, (TEXT("ChrgApp: fuction_TB:0x%x \r\n"),(UINT32)data));


	//_swi();		//swi test
	//data=INREG32(0x70003020);//data abort test


	OEMIntrInit();

	_enable_irq();//enable irq

	IPLTimerStart();

	RETAILMSG(INTR_INFO, (L"-IPLIntrInit\r\n"));

}

//------------------------------------------------------------------------------
//
//  Function:  IPLIntrRequestIrq
//

void  IPLIntrRequestIrq(UINT32 irq,ISR isrhandler)
{
	g_isr_func[irq]=isrhandler;
}

//------------------------------------------------------------------------------
//
//  Function:  IPLIntrEnableIrq
//
BOOL IPLIntrEnableIrq(const UINT32 irq)
{
	BOOL rc = TRUE;
	UINT32 mask;

	RETAILMSG(INTR_INFO, ( L"+IPLntrEnableIrq(0x%x)\r\n",  irq));

	// Depending on IRQ number use internal or external mask register
	if (irq <= IRQ_ADC)
	{
		CLRREG32(&g_pIntrRegs->INTMSK, 1 << irq);
	}
	else if (irq <= IRQ_EINT7)
	{
		CLRREG32(&g_pIntrRegs->INTMSK, 1 << IRQ_EINT4_7);
		CLRREG32(&g_pPortRegs->EINTMASK, 1 << (irq - IRQ_EINT4 + 4));
	}
	else if (irq <= IRQ_EINT23)
	{
		mask = 1 << (irq - IRQ_EINT4 + 4);
		OUTREG32(&g_pPortRegs->EINTPEND, mask);
		CLRREG32(&g_pPortRegs->EINTMASK, mask);
		mask = 1 << IRQ_EINT8_23;
		if ((INREG32(&g_pIntrRegs->INTPND) & mask) != 0)
		{
			OUTREG32(&g_pIntrRegs->INTPND, mask);
		}
		CLRREG32( &g_pIntrRegs->INTMSK, 1 << IRQ_EINT8_23);
	}
	else
	{
		rc = FALSE;
	}

	RETAILMSG(INTR_INFO, (L"-IPLIntrEnableIrq(rc = %d)\r\n", rc));
	return rc;
}

//------------------------------------------------------------------------------
//
//  Function:  IPLIntrDisableIrq
//
VOID IPLIntrDisableIrq(const UINT32 irq)
{
	UINT32 mask;
	RETAILMSG(INTR_INFO, (L"+IPLIntrDisableIrq(0x%x)\r\n", irq));
	if (irq <= IRQ_ADC)
	{
		// Use interrupt mask register
		mask = 1 << irq;
		SETREG32(&g_pIntrRegs->INTMSK, mask);
	}
	else if (irq <= IRQ_EINT23)
	{
		// Use external mask register
		mask = 1 << (irq - IRQ_EINT4 + 4);
		SETREG32(&g_pPortRegs->EINTMASK, mask);
	}

	RETAILMSG(INTR_INFO, (L"-IPLIntrDisableIrqs\r\n"));
}

//------------------------------------------------------------------------------
//
//  Function:  IPLIntrDoneIrqs
//
VOID IPLIntrDoneIrq(const UINT32 irq)
{
	UINT32 mask;

	RETAILMSG(INTR_INFO, (L"+IPLIntrDoneIrq(0x%x)\r\n",irq));
	if (irq <= IRQ_ADC)
	{
		// Use interrupt mask register
		mask = 1 << irq;
		//OUTREG32(&g_pIntrRegs->SRCPND, mask);
		CLRREG32(&g_pIntrRegs->INTMSK, mask);
	}
	else if (irq <= IRQ_EINT23)
	{
		// Use external mask register
		mask = 1 << (irq - IRQ_EINT4 + 4);
		OUTREG32(&g_pPortRegs->EINTPEND, mask);
		CLRREG32(&g_pPortRegs->EINTMASK, mask);
	}
	RETAILMSG(INTR_INFO, (L"-IPLIntrDoneIrq\r\n"));
}


//------------------------------------------------------------------------------
//
//  Function:  IPLIntrDeinit
//
//
void IPLIntrDeinit()
{
	BOOL rc = FALSE;

	RETAILMSG(INTR_INFO, (L"+IPLInterruptDeinit\r\n"));



	_disable_irq();//disable irq

	RETAILMSG(INTR_INFO, (L"-IPLInterruptDeinit\r\n"));

}

